{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 作业一"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 明确工作路径\n",
    "import os\n",
    "d = os.path.dirname('.')\n",
    "run_dir = os.path.abspath(d)\n",
    "os.chdir(run_dir)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 读取数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2051, 60000, 28, 28)\n",
      "(60000, 28, 28)\n"
     ]
    }
   ],
   "source": [
    "# 解包拆包\n",
    "# 读取数据方法一\n",
    "# 通过.ubyte文件读取数据\n",
    "import numpy as np\n",
    "import struct\n",
    "with open('{}/MNIST_data/train-images-idx3-ubyte'.format(run_dir), 'rb') as f:\n",
    "    buffer = f.read(4*4)\n",
    "    head = struct.unpack('>iiii', buffer)\n",
    "    print(head)\n",
    "    length = head[1] * head[2] * head[3]\n",
    "    data_byte = f.read(length)\n",
    "    data = struct.unpack('>{}B'.format(length), data_byte)\n",
    "imgs_np = np.reshape(data, (head[1], head[2], head[3]))\n",
    "print(imgs_np.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "float64\n",
      "object\n",
      "(70000, 784)\n",
      "(70000,)\n"
     ]
    }
   ],
   "source": [
    "# 通过.mat文件读取数据\n",
    "from sklearn.datasets import fetch_openml\n",
    "mnist_data = fetch_openml('mnist_784')\n",
    "x = mnist_data['data']\n",
    "y = mnist_data['target']\n",
    "print(x.dtype)\n",
    "print(y.dtype)\n",
    "print(x.shape)\n",
    "print(y.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('int64')"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 改变标签数据类型\n",
    "y = np.array(y, dtype=np.int64)\n",
    "y.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 查看数据样本\n",
    "%matplotlib inline\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "some_digit = x[36000]\n",
    "some_digit_image = some_digit.reshape(28, 28)\n",
    "plt.imshow(some_digit_image, cmap=matplotlib.cm.binary)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 上图所示，该样本标签是9\n",
    "y[36000]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 划分训练集和测试集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(60000, 784)\n",
      "(60000,)\n"
     ]
    }
   ],
   "source": [
    "x_train, x_test, y_train, y_test = x[:60000], x[60000:], y[:60000], y[60000:]\n",
    "print(x_train.shape)\n",
    "print(y_train.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 洗牌\n",
    "np.random.seed(42)\n",
    "shuffle_index = np.random.permutation(60000)\n",
    "x_train = x_train[shuffle_index]\n",
    "y_train = y_train[shuffle_index]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 建立二元分类标签"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 根据上面数据样本标签表示9，所以设置二元分类，True为9，False为非9\n",
    "y_train_9 = (y_train == 9)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 二元分类器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([False])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 选取SGD为二元分类器\n",
    "from sklearn.linear_model import SGDClassifier\n",
    "sgd_clf = SGDClassifier(random_state=42)\n",
    "sgd_clf.fit(x_train, y_train_9)\n",
    "a = sgd_clf.predict([some_digit])\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 性能评估"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 准确率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.9489 , 0.93405, 0.95105])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用交叉验证得到准确率\n",
    "from sklearn.model_selection import cross_val_score\n",
    "accuracy = cross_val_score(sgd_clf, x_train, y_train_9, cv=3, scoring='accuracy')\n",
    "accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.90195, 0.9007 , 0.8999 ])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 验证准确率是不是首要模型性能指标\n",
    "from sklearn.base import BaseEstimator\n",
    "class Never9Classifier(BaseEstimator):\n",
    "    def fit(self, x, y=None):\n",
    "        pass\n",
    "\n",
    "    def predict(self, x):\n",
    "        return np.zeros((len(x), 1), dtype=bool)\n",
    "\n",
    "n9c = Never9Classifier()\n",
    "accuracy = cross_val_score(n9c, x_train, y_train_9, cv=3, scoring='accuracy')\n",
    "accuracy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 小结1\n",
    "  * 通过上述测试，在所有标签都为False的情况下，准确率还能达到90%以上，这说明正样本数在总样本中比重很小，因此，用准确率还衡量性能不准确"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 混淆矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[52133,  1918],\n",
       "       [ 1402,  4547]], dtype=int64)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.model_selection import cross_val_predict\n",
    "from sklearn.metrics import confusion_matrix, precision_score, recall_score\n",
    "y_train_pred = cross_val_predict(sgd_clf, x_train, y_train_9, cv=3)\n",
    "c_matrix = confusion_matrix(y_train_9, y_train_pred)\n",
    "c_matrix"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 精度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.7033255993812838"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p_score = precision_score(y_train_9, y_train_pred)\n",
    "p_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 召回率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.7643301395192469"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r_score = recall_score(y_train_9, y_train_pred)\n",
    "r_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### F1分数 谐波平均值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.7325600128886741"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.metrics import f1_score\n",
    "f_score = f1_score(y_train_9, y_train_pred)\n",
    "f_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### PR曲线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.metrics import precision_recall_curve\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']\n",
    "\n",
    "# 自定义PR曲线函数\n",
    "def plot_precision_vs_recall(y_true, y_scores):\n",
    "    precisions, recalls, thresholds = precision_recall_curve(y_true, y_scores)\n",
    "    plt.plot(recalls, precisions, \"b-\", linewidth=2)\n",
    "    plt.xlabel(\"召回\", fontsize=16)\n",
    "    plt.ylabel(\"精度\", fontsize=16)\n",
    "    plt.axis([0, 1, 0, 1])\n",
    "    plt.show()\n",
    "\n",
    "y_scores_sgd = cross_val_predict(sgd_clf, x_train, y_train_9, cv=3, method='decision_function')\n",
    "plot_precision_vs_recall(y_true=y_train_9, y_scores=y_scores_sgd)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 通过选择阀值来实现最佳的精度/召回率权衡"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXMAAAENCAYAAAD9koUjAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nO3dd3wU1drA8d9JgSQQIPRQQpcqhBAQpUWaYEUUVISrSLlWXtvFLliuvVyuV1AUG1aQoggoIgJSlRil914DIRAgISTZ8/5xstkQNskmbHa2PF8++5nJzJmdZyfJw+TMKUprjRBCCN8WZHUAQgghLp4kcyGE8AOSzIUQwg9IMhdCCD8gyVwIIfyAJHMhhPADksyFEMIPuJTMlVK1lFK/FbE/VCk1Rym1XCl1l/vCE0II4Ypik7lSKgr4FKhQRLEHgEStdRfgZqVUpJviE0II4QJX7sxzgFuAtCLKJADTcteXAvEXF5YQQoiSCCmugNY6DUApVVSxCsCB3PXjQK2CBZRSo4HRABUqVOjQokWLksZKakYqJzNPYtO2wuPF+fAERQ1b4I5jtNZ55fPvy1vXFx5X1D6btqEp8J4BMPJCkAoiNDiUkKAQgoOCCVbBKKUIVmY9SAURHBRMaFAoQUFBppwKJiQohCAVhFIKRZE/q0K41blzsG4dBAVB1aqO7ZGRjq8zM+HwYShXDqKjS3+uxMTEY1rrGs72FZvMXXQaCAdOAhVzvz6P1noyMBkgPj5er1mzxk2nDiw2bSPHlkOOziEzO5PUs6lorbFpW6EvzYX7s3KyyMjOKPZYZ8fbj822ZV/wyszOJD0rnRydkxdn3lLnkG3L5lzOOU6fO83Z7LOczT5LWmYaJ8+e5MTZE2RkZ5CZ+6+0glQQtSvWpl6lelQLr0aDyg3oUKcDrWu0pnXN1lQqX8mN3xER6DZvhpYtoWlT2LKlbM+llNpT2D53JfNEoCvwLdAOWOWm9xUFBKkggoKDCCWUsJAwKodVtjokt9Fak5aZxsFTB0k9m0pKegoZ2RmczT7LybMnOX3uNKfPnebw6cOknUsjLTONlPQUTpw9wfGM42TmZHI2+yw2bePgqYMcPHXQ8eaJjtWosChqVKhB06pNuaTqJdSsUJP20e1pVKUR0ZHRkuxFidSpA9OnQ1iYtXEoV0dNVEot1lonKKV6Aq201v/Lt68BMA9YCFwBdNZa5xT2XnJnLspSZnYmh04f4kDaAVIyUth0dBOrD6xmS8oWtqVsI8uWVeTxtSrUol/TfnSq24kO0R1oH92ecsHlPBS9EIVTSiVqrZ0+k3Q5mbtwkjqYu/OftNYniyoryVxYRWvN0fSjHDp1iM3HNrMvbR97T+5lXfI69pzYw6HThzibffa8Y0KCQmhTsw39m/ZnzGVjqF2xtkXRi0DnkWReEpLMhbeyaRtJh5L4be9v/HnoT1buX8n249vz9ocEhdC5XmduankTN7e6mXqV6lkYrfAG27bBt99CixZw441ley5J5kJchNSMVJbsWcKUpCn8sPWH8/a1qN6CO9rdwc2tbqZp1aYWRSisNGsWDBwIN9wAs2eX7bmKSubSnV+IYkSFRzGgxQDm3DaHgw8fZOqNUxnQYgDhIeFsPraZJ355gmbvNKPpf5vyyE+PkJqRanXIwoPO5tbKWf0A1F2tWdwuLS2N5ORksrKKflgl/EtISAhhYWHUqFGDMKt/O5yIjoxmaNuhDG07lKycLL7b8h1frf+Kn7b/xI7UHby16i3e/eNdBrcezFPdnqJ59eZWhyzKmD2Zh4dbG4dXJvO0tDSOHDlC3bp1CQ8PL67DkvATWmuys7M5ffo0e/fupVatWlSu7L1NL0ODQ7m51c3c3OpmsnKyWLx7Mc8teY7l+5Yzde1Upq6dSt8mfXmz75u0qdnG6nBFGcnIMEur7z28spolOTmZunXrEhERIYk8gCilCA0NJSoqinr16pGSkmJ1SC4LDQ6lT5M+LLtrGRvu3cDg1oMJUkEs2LGAdu+147GfHyMjK8PqMEUZ8JZqFq9M5llZWYRb/TeLsFR4eDiZmaXvBWqlVjVa8c3N37D/of3c3OpmbNrGayteo+17bfnPqv+QllnUMEfC10gyL4bckQc2f/j+R0dGM33QdGbdMotGVRqx/fh2HvrpIdq/356dqTutDk+4SUgIVKlixmKxktcmcyH8xYAWA9h430Y+uO4DmlVtxs7UnbSd1JbvNn9ndWjCDcaOhdRUePJJa+OQZO4BixcvNqP55b6io6MZMWIEJ08W2VHW7Ro2bMgnn3xi2fGBLCwkjJFxI1kxYgV9m/TlTNYZBk4byNfrv7Y6NOEnJJl70Pfff8/q1at54403+PXXXxk+fLhHzz9nzhyuu+46y44XUD2iOvNvn8/w2OHYtI2hM4fyyrJXrA7LbbSGtNxHAgcPwsKF1sYTSLyyaaK/uvTSS2nYsCGdOnWiYsWKDBgwgOTkZGrWrOmx81t5vDCCVBAfXv8hCsVHf33EE788QfWI6oyMG2l1aKVis0FwMDRsCLt3X7h/4kS45x5PR+U5Y8bADz/A22+bXqBWkTtzizRvbjqT7Hb20y/8XpAKYsoNU3i2+7MAjJozimGzhvlM88VPPoHRo0Epk8jBeSIHuPdeeP55k/Cs7gO4eTNccw3c5caZig8dgl27zAQUVpJkbpHDhw8DEB0dzfjx40lISCAlJYXhw4dTvXp19uzZc17ZQYMGUbFiRWJiYnj55ZfPmwVp586dXHPNNURERBATE8O4ceOw2S6cjamwOu+NGzfSq1cvIiMjqV27NmPHjnU6y1Jhx+/du5frrruO8PBwGjRowKRJk/L22Z8X7Nu3j969e1OhQgXi4+PZunVrSS6X3xqfMJ6Xer4EwOdrP2fE9yOKnOHKak8/bRL48OHwwQcX7s8/i84r+WqPxo2D664zM+08+6yZdccKn30G8+bBd9+ZKiF38JYeoD6XzJUq/DV5sqPc5MlFl82vQ4fCy40e7SiXmMhFs9lsbNu2jXHjxtGxY0fq1TOj7mVmZtKzZ0/OnDnD+PHjqZo735TWmoEDB7J//35mzZrFc889x/PPP8/7778PwLlz5+jXrx8nTpxg9uzZ/Pvf/+btt9/mzTffdDmmW2+9lZycHObMmcNrr73Ge++9x5dffunSsdnZ2Vx99dUcPXqU2bNnc++99/J///d/fPXVV+eVu/baa+nWrRszZswgJSWFxx57zOX4/JlSiie6PcHSO5cSHhLOV+u/4oM/nWRJi23dan4f/v3vwsukpZl6cq3N67HHTOIs6IUXTNJfvLjMwi3UnDlmefw4DB4M7uiX5i09QKXO3IMaNWqUt96+fXu+/vrrvPbUq1atYuzYsbz66qvnHfPrr7+yatUqduzYkXf80qVL+eKLL7j77rv58ssv2b9/P8uWLcure09NTSU11fXBnvbu3cvQoUNJSEgAoEmTJlSvXt2lY7/++mt27NjBzp07iY6O5qqrruLgwYM8++yz3HbbbXnlrrrqKsaNGwfA/fffz3vvvedyfIGgW4NuvNP/HUbOGcmzvz7LoFaDiAqPsjos1q+Hwh6VLFsGrVubNtaF6d/fJPZPP4U77zx/35VXXvzd8fLl8PLL5q8EV+bWzH/3/O235jPMnw+xsaWPQToNlZL9f31nr/x30aNHF102v8TEwsvlv9vv0OHiYp87dy5//fUXhw4d4s8//+SSSy7J21ejRo28ZJffhg0b0FrTuHHjvKaNn3zyCTt27ABg3bp1NG/e/LyHqGPGjHH6XoW55557eOaZZ7juuut48cUXqVKlSl6dfnH++usvmjdvTnS+36SEhAS2b9/O6dOOqWBH5/vmVKtWTQZQc+LO2DvpEN2BI2eOMPbnsVaHAzhP5Dab+d3o0qXoRJ7fHXeY4xYtgpkzHdvPnnVeXmvYu7f4933kEZg7F/7888J92dkwZcr5dfnJyWa5cKGJ//BheP111z5DYSSZB6BWrVrRrl07ate+cKaaVq1aERERccF2rTWVK1cmKSnpvNeCBQsKPc+RI0dYsWKFy3G9/PLLLFu2jC5durBo0SLatm3LzPy/ccUo2FvT/nX+ut/8f5UI54KDgpl83WRCg0L5MOlDpm+Yblkszqojjx0zSba0nXOVMnfjN97ouDFy9mOWlQUTJsDttxf/nt27m+XUqRfu++orGDny/Akjjh41y06d4IknzPrx465/Bmekzly4pHXr1pw8eZJq1aoRGxtLbGwse/fuzaumaNOmDVu2bOHYsWN5x0ycOJGhQ4e69P7p6ek8+OCD1KtXj8cff5xFixbRp08flzsHxcbGsnnzZo4cOZK3bcmSJTRp0oTIfP2bg+1NHkSR4qLjeLW3qWp75tdnsOkLH2SXtfzJunt3x1+p1aq57xyjRpll7qMf1q2D/fvN+pkz8OijsHq1oz7amR07HJNBfPONqcrJr3x5s2zWzPG+6enmDrpiRejWzZx3ypSL+ywjR8K//gUeamFcKEnmXq5nz5507tyZQYMGMX/+fGbOnMk999yTd9c7ZMgQ6taty4033siCBQv47LPPeOeddxgxYoRL7x8REcGcOXN46KGHWLJkCbNnzyYpKcnlO+lbbrmFxo0bc+ONN/LTTz/x+uuv8+677/LCCy+U+jMHuvs73U+9SvXYkrKFt1e+7dFzT5hw/tf/+5/zchdryBB46inTxPHUKXOnXL++eYC6YgXk5Jg79IIJOr9jx8yUbXYjR55fNXPunFmGhppz2KtYatY0/2FVqgRt2kCdOmZ7ae/QH3wQXntNkrkohlKKWbNmUb9+fQYNGsQ999zDoEGD8lqrlC9fnp9++olKlSpxww038Oyzz/Lwww/z+OOPu3yO2bNnk5KSwg033MDw4cPp1asXzz//vEvHhoaGMn/+fKpWrcqAAQN45513mDBhwnkPP0XJhAaH8vZVJomPXzKeHcd3eOS8WpvEZJedXfjDz4sVGWnanq9da5Kqvaqibl3TDtzujTfg449N0q9d2zyQtbP/MdivH/Tta+Jdtsx8jn37HG3aMzLMXxhDh5o78ho1Loxn5kxo3BiKqL30flprj786dOigi7Jx48Yi94vAEMg/BzabTV/75bWa8eh+n/fz2HntlSqJiWV/LputqCYKzl/t2jmOf/dds23UKK3vuUfrcuW0/vFHrZ94wmzv2dMsmzfXunx5s/7WW1pnZV0Yy113mf1hYVofOVKyzzFvntYLF2qdk3Nx18MVwBpdSF6VO3MhvJBSiolXT6RiuYr8uP1HVuxz/YF2abz6qql6eOUVU48cF1empwPM+erXL9kxf/9t7rRtNkcVS/36cNttpofpVVeZpopgWs4A9O7tqBefNs0MWVvQBx9A587mLwRnLWMKozVce605h9V9vSSZC+Gl6leuzwOdHgBg/OLxZXoue63c44+bemRPSUpyrD/7LAwb5vg6t9vDBSIizBAC//mP+ToryzzM7NPHfN279/nlQ0PNmCmhoeahapqTuUGCghxtzbdscT3+rCzzH0toqGNYA6tIMhfCi425bAzlg8vz886fWbZ3WZmcI3/TvXwjMXhEtWqmhcl//wt33w3vvWcGrProI9OZ55lnin+P/PED/PwzXH+94+srrjB15VlZ5u75ueecv4+920dJkrm3tDEHSeZCeLXaFWvz8OUPA/DGijfc/v6ZmY7mfWASqqeFh8MDD5genBER5iHs8OEmQT7/vGm1smAB/P67aYJoV6+e6RDUvr35+rHHzHu9/Ta0bWu2jRgBgwadf75y5ZzHYe8nl7+FTHEkmQshXPZApwcICQrh+y3fs/nYZre+9zvvONZ/+cWtb+021aqZKpSOHaFlS1MlAmZo3QYNHOXq1XPUeQ8caNqZV6vmqMtOTDTD1RbW0KtWLbMsSRNFSeZCCJdFR0Zz+6W3o9E886sL9Q4lcPnlZtmnD/Ts6da3LhOXXmraoGttRmHMz96r9PPPzQPc//3P8WAXzLYJE6ByZefv3bKlufv/9lvX47F3arK69ydIMhfCJzzbw4x7PmvTLFLS3TDUH6ad9tmzJjH6dPvqXHFx5u7czt4ZyFUREebuvyQjT8iduRCiRBpHNSahYQI5OofZm2cXf0AxkpNNJ5zevd0zDKw3CAszDz/tnHUOcrdWrUyvU/vQulaSZC6Ejxh6qRlv518//4tj6ceKKV04rR31w2Ca1fmLFi3MzD/z50N8fMmPf/RRuOkmWLXKtfKhoaade0xMyc/lbpLMPeCTTz7JG742KCiIBg0a8Oijj3LmzBmPxpGQkMD48eNLvV9Y6672d9GpbidSz6YyOXFy8QcU4oEHHOsjR5ru9P6kdm3Txb80ozueOGG69l9zzflDB/gCSeYe9Mcff7Bs2TIeeugh3n//fe62oh2Y8FlKKcb1MOPUv/P7O2Tbskv8Hjk58O67jq+dTf0WyCZOND06jx+HXr2Kb3O+YgUMGAAlmNirzEgy96D4+HiuuOIKHnzwQR5//HGmTZtGptWzwAqf0r9pfxpHNebw6cPM3Tq3xMf//bdj/bvv3BiYnyhXDqZPN617kpNNQt+5s/Dye/aY6/jHH56LsTCSzC0SFxfHuXPnSPGXp0/CI5RS3N3B/EX3+brPS3Ts6dNmmrVTp8xY4Pl7SQqHsDDTkap7dzhwwDTZLGzWI59rzaKUmqKUWqmUerqQ/VFKqXlKqTVKqffdG6J/OnLkCEopqlWrxqlTpxg1ahRRUVHUrl2bhx9+mHP2wZiBY8eOMWTIECIjI4mOjuaBBx4gPT09b/+BAwcYMGAAlStXpnbt2jz00EPYbJ6f1EB4xs2tbgbgh60/kHwm2eXjIiNNp5mPPzbDvYrCRUSYgbsuu8w04dy+3Xk5b0rmxU7orJQaCARrrS9XSn2klGqmtS7Y4XUY8IXW+gul1JdKqXit9Rp3B6ueK+V8VW6mx13c8GgbNmzglVdeoVevXpQvX5477riDpKQkpk6dSkZGBvfeey/lypXjlVdeAeCmm24iOTmZL774guzsbMaMGYPNZuPd3MrP22+/nZSUFGbOnMmJEycYPXo07dq1486CM+gKv9AoqhFXN7uaedvm8d6a9/LaoBdlwwbH+g7PDI/u8yIj4ccfYeNGM76LM94yZRy4kMyBBGBa7voCoCtQMJmnAG2UUlWA+sA+dwXoT/LPlRkXF8eUKVPYuXMn33zzDYsXL6ZHjx4AbNy4kQ8//JBXXnmFxYsXs3TpUtatW0eb3OHsMjMzWbbMDLqktWbIkCF06dKF1q1bk52dzbvvvsvq1aslmfuxMZ3GMG/bPL5Y94VLyTz/A7q3PTt5kU+rUuX8RH7smOlBam/Oae8B6hN35kAF4EDu+nHA2UjHy4BrgDHAptxy51FKjQZGA8SUslHmxd4RWy0pKYmDBw9y7bXXMnbsWGJiYpiT29sgwcl4n+fOnWPdunVUqVIlL5ED3HbbbXkz+SilGDx4MB999BFjx45l1apVpKamlvoaC9/Qq3EvqoRVYWvKVralbKNZtWaFls3KMlUrAC++WPoJmQPda6/B+PEwebKZtQi8q5rFlTrz04D9j4iKhRwzDrhba/08sBkYXrCA1nqy1jpeax1fwxNds7xQbGwsV199Nddffz2vvmom7dW5owAtX76cpKSk814hzkbRB06cOMGyZcs4d+4cp06dIi4ujmnTpnHVVVcxd+5chuUfFFr4pZCgEK5pZuZXm5JU9IzE2dlmPO9WrRwz0ouSq17d3Im/8ooZwxygSRMzIUazwv8v9RhXknkipmoFoB2w20mZKOBSpVQwcBng27fQZezJJ58kKSmJn3/+mdatWwNgs9mIjY0lNjaWjIwM3nrrLbKzs2nTpg0nTpxgQ75Kz1mzZtGrVy+UUvzyyy/s2rWL+fPnM2bMGDp37sz2wp7WCL/yzw7/BGDahml5NwUFaW3GKB8zxtT/Bkn7tVK7/XYzR+mGDTBvntl2xx3mug4ZYm1s4Foynw0MU0q9BQwGNiilXixQ5mVgMnASqAp85dYo/UynTp3o1asXr776Kk2aNOGWW25hxIgRzJw5k/nz5zNy5EhSUlIoV64cV155Jd27d2fw4MHMmTOHGTNmMH78eP7xj38QGhpKtWrVAPj444/5+eefGTBgACtWrCA7u+QdSoRv6RLThVoVarHrxC7+PvK30zKdO8Mjj8CmTSWfok2cr3x5eNgMLU9u2wSvUmwy11qnYR6CrgKu1Fr/rbV+ukCZ37XWrbXWFbXWfbTWp8smXP/x1FNP8csvv7BmzRo++OADunXrxogRI7j99tuJj49n6tSpeWVnzJhBu3btGDJkCGPGjGHgwIFMmDABgG7duvHUU0/x0ksvMWzYMKKiovjnP//J6tWrycnJserjCQ8IUkHc0PwGwIymWJDNZoZ0Be/ooegPRo2CqCjTXn/ZMtNT9OhRyNeS2DKqsD/PylJ8fLxes6bwloubNm2iZcuWHoxIeCP5OSjej9t/pP8X/YmLjiNxdOJ5+/73P8c4LKmppmWGuHjPPgsvvGDGbwkPN+Off/MNDB5c9udWSiVqrZ0OISY1aEL4sB4NelA+uDxJh5IuGEnRnsgrVJBE7k4PPGCS+LlzcPKk2eYrrVmEEF4qPDScrjFd0WgW7VqUt31yvkEVP/3UgsD8WI0aZp7QBQscU9JJMhdCXLTejXsDsHDnwrxtUVGO/Tfd5OmI/F/dumbpTT1AJZkL4eOcJfObbzYPP7OyrIoqMOR2xJY786JY8WBWeA/5/ruufe32RIVFsevELnam7uTMGTNueceOUEi/M+EGu3Y51suVsy4OO69M5qGhoWTYBz0QASkjI4Py5ctbHYZPCA4KpmejngDMXruQihXN2CHS1aBsNWoEDz4Il19upquzmlcm85o1a3LgwAHS09PlDi2AaK3Jysri+PHj7N+/P69DlChen8Z9AHjxS8eMxnJXXvbeftvMNuQN9x1e+e2ulDsp4cGDB8mSSr+AEhISQlhYGDExMYR5Q0Wkj7DXm6dWWQQqh/vuDbY4IuFpXpnMwST0Sv4206wQZaRxVGMq2xpyMmI3RCcxYUIppqYXPs0rq1mEECWjlOJkkqlqCW07k2C5MQ84ksyF8AM7dgDrbwGgQZ+ST/QsfJ8kcyH8QFYWsLcrZIWx/dRajp45anVIwsMkmQvhB5o3h5nTy9O1oZnjbMmeJRZHJDxNkrkQPi49Hd57z8wm1LdZAgBL9yy1NijhcZLMhfBx995rhrs9ehQur385AL8f+N3iqISneW3TRCFE8Ww2x6iIGzZA+87tAVifvB6bthGk5H4tUMh3WggfNnOmY/3KK6FaRDXqVarHmawzbE3Zal1gwuMkmQvhwwYNMssqVUApsx5fx3QYkqqWwCLJXAgftW+fYz3/LIyX1b0MgNX7V3s4ImElSeZC+KhPPnGsN2niWL+8nnkIunL/Ss8GJCwlyVwIH7VqlVlOmnT+9o51OxKsgvn7yN+cPnfa84EJS0gyF8JHTZ4Mb70Fd955/vaI0AjaR7fHpm1Sbx5AJJkL4aPq1oWHHnI+ZdkV9UxP0OV7l3s4KmEVSeZC+KAnn4T//Kfw/V1iugCwYv8KD0UkrCadhoTwMXv3wssvm/X773c+o9AV9c2d+cp9K6XzUICQ77AQPua77xzrhU0NV69SPepXqs/JzJNsPLrRM4EJS0kyF8LHzM0drjx/00RnOtfrDMAfB/4o24CEV5BkLoQPOXMGFi82vT379y+6bIfoDgD8cVCSeSCQZC6ED1m0CDIzoWNHqFmz6LL2O/MV++QhaCCQZC6ED7FXsVxzTfFlO9XtRLngcqw9spbUjNSyDUxYTpK5ED5k0yazvPrq4suGh4YTXycejWbV/lVlG5iwnCRzIXzI4sWwcSPExblW3j7olvQE9X+SzIXwIUpBy5YQ5OJvbteYrgDM3z6/DKMS3kCSuRA+4vjxkh/Tp3EfFIrEQ4lkZGW4PyjhNSSZC+ED0tKgTh3o3BnOnXP9uMjykbSp2YZsWzZ/Hvqz7AIUlnMpmSulpiilViqlni6m3ESl1HXuCU0IYTd/vmmSGBoK5cqV7Fh7135poujfik3mSqmBQLDW+nKgsVKqWSHlugG1tdZz3ByjEAFv9myzHDCg5MfaOw8lHU5yY0TC27hyZ54ATMtdXwB0LVhAKRUKfADsVkrd4LbohBBkZjral994Y8mPj4s2TV8kmfs3V5J5BeBA7vpxoJaTMv8ANgKvAZ2UUg8ULKCUGq2UWqOUWnP06NHSxitEwFm0CE6dgrZtoXHjkh/fpmYbQoJC2HJsi8w85MdcSeangfDc9YqFHNMemKy1Pgx8DlxZsIDWerLWOl5rHV+jRo3SxitEwLmYKhaA8iHlaV2jNRrN2iNr3ReY8CquJPNEHFUr7YDdTspsB+z3DPHAnouOTAiB1vDjj2a9NFUsdvaqFmnR4r9cSeazgWFKqbeAwcAGpdSLBcpMAa5USi0F7gXecG+YQgQmpWDdOpg5E9q1K/37tK/dHpBk7s+KnWlIa52mlEoA+gCv5Val/F2gzClgUJlEKESAq1Tp4u7KQR6CBgKXpo3TWqfiaNEihPAQrc3d+cVqV7sdCsX65PWczT5LWIiTWaCFT5MeoEJ4qZ07oXZtuOeei3+viuUq0qJ6C7Jt2aw7su7i31B4HUnmQnipBQsgORnc1ZK3Qx3TeSjxUKJ73lB4FUnmQnipX34xy7593fN+edPIyZygfkmSuRBeKCsLFi406717u+c988Zo2S9jtPgjSeZCeKEVK+DECTN2eWl6fTrTtlbbvJ6gpzJPuedNhdeQZC6EF/rhB7O89lr3vWdYSBjtarVDo1lzcI373lh4BUnmQnihskjmYCZ5BiSZ+yGX2pkLITzriy/MGOZXXOHe942vEw/AHwflIai/kWQuhBeKi3N90uaSsN+ZSzL3P1LNIkQAaVm9JRVCK7D7xG6OnpGhqP2JJHMhvEhaGvToAa+/brryu1twUHDeOC1Sb+5fJJkL4UUWLIClS2HOHPeMyeJMxzodAUnm/kaSuRBepKxaseRnfwi6bN+ysjuJ8DhJ5kJ4iZwcmDfPrF9zTdmdp1fjXgAs2b2EMzFk/JQAABk6SURBVOfOlN2JhEdJMhfCS/zxhxlUq2FDaNWq7M5Ts0JNYmvHkpmTKa1a/IgkcyG8RP4qlrKqL7frVMc0UZSZh/yHJHMhvIQn6svt7PXmMhyu/5BOQ0J4Aa3h0UfN5M09epT9+fLGNj8oydxfyJ25EF5AKRg6FD7/HMI8MKNb6xqtCQkKYdvxbaRnpZf9CUWZk2QuRAAqH1KeNjXbYNM2qTf3E5LMhbDY4cNw552waJFnz9umZhsANh7d6NkTizIhyVwIi/30E3z6KbzxhmfPG1srFpCeoP5CkrkQFvv+e7Ps18+z57U/BF17ZK1nTyzKhCRzISyUlgZz55oHoDfd5Nlz26tZ1ievJ8eW49mTC7eTZC6Ehb7/HjIzoWtXqFvXs+euHlGdhlUacibrDBuObvDsyYXbSTIXwkJTp5rlrbdac/7O9ToDsHLfSmsCEG4jyVwIixw8CAsXQmiodcn8inpmXrrl+5ZbE4BwG+kBKoRFQkLgySfh5EmoWtWaGOwjKM7dNpdsWzYhQZISfJV854SwSM2a8MIL1sbQsnpLYirHsPfkXjYd3cSltS61NiBRalLNIkQAU0pxeb3LAVi1f5XF0YiLIclcCAv873/w5ptm/HKr2R+CSr25b5NqFiE8LCcHXn7ZPADt3Blq1LA2ni71uwCwcr+0aPFlcmcuhIf9+qtJ5I0bwxVXWB0NxNaOpUJoBbambOVA2gGrwxGlJMlcCA/7/HOzHDq07GcUckVocCg9GppB1JfsWWJxNKK0XErmSqkpSqmVSqmniylXSymV5J7QhPA/6ekwY4ZZHzrU2ljys1e1LN8r9ea+qthkrpQaCARrrS8HGiulmhVR/A0g3F3BCeFvZs+G06fhssugWVG/SR6Wl8zlIajPcuXOPAGYlru+AOjqrJBSqidwBjjslsiE8EMff2yWw4ZZG0dBHet2JCQohHXJ60jLTLM6HFEKriTzCoD9qchxoFbBAkqpcsAzwOOFvYlSarRSao1Sas1Rb2iPJYQF7rsPrr8ehgyxOpLzRYRGEFs7Fpu2kXRIakp9kSvJ/DSOqpOKhRzzODBRa32isDfRWk/WWsdrreNrWN0WSwiLDBgA330HUVFWR3Kh5tWaA7Dt+DaLIxGl4UoyT8RRtdIO2O2kTG/gPqXUYiBWKfWhW6ITQnhMbG0z89AfB/6wOBJRGq4k89nAMKXUW8BgYINS6sX8BbTW3bXWCVrrBOAvrfVI94cqhO/69lu4/XZITLQ6ksLZu/VL5yHfVGwPUK11mlIqAegDvKa1Pgz8XUT5BLdFJ4Qf0BoefBAOHDCTUHToYHVEzsVFxxEaFMr65PWcyjxFZPlIq0MSJeBSO3OtdarWelpuIhdClMCSJSaRg3e1LS8oPDSc2NqxaDS/H/jd6nBECUkPUCHK2JtvmuW4cRDp5Te7UtXiuySZC1GG/voLfvjBTERx331WR1O8y+tLMvdVksyFKEP2apVu3awfHdEV+cc211pbHI0oCUnmQpSR9HTYkDvp/auvWhuLq2Iqx1C7Ym2OZxyX9uY+RpK5EGUkIgL274fJk6FjR6ujcU3+mYdW7pOqFl8iyVyIMlS3LowaZXUUJWNP5iv2rbA4ElESksyFKAOrV5sZhXxRQsMEAH7a8ZO1gYgSkWQuhJtt2WI6B8XGQmam1dGUXFx0HJXLV2bPyT3sSt1ldTjCRZLMhXCzsWMhO9vM71m+vNXRlFxwUDBdY8xwTKsPrLY4GuEqSeZCuNGSJfD991ChArz4YvHlvdVldS8DYPV+Sea+QpK5EG6SmQkJCWZ97FiodcHI/77jsnq5yVzuzH2GJHMh3CQszLE+dqx1cbhDxzqmLeWfh/7kXM45i6MRrpBkLoSbjBhhlgMGnJ/YfVFUeBSXVLuEzJxM1h5Za3U4wgWSzIVwkw8/hIwMmDXL6kjc44r6VwDw665fLY5EuEKSuRAX6aef4FxuTYSv35Hnd22zawGYsWmGxZEIV0gyF+IirF0LN9xgXjab1dG4V7+m/QgJCuGPg3+QnpVudTiiGJLMhSil9HQzKmJmJsTEQJCf/TZVKFeBFtVbYNM2mRfUB/jZj58QnjNmDKxbB5dcAm+8YXU0ZaNfk34AfLvxW4sjEcWRZC5EKXz+OUyZYurIp0/3/hmESuvWNrcCMH3jdBnf3MtJMheihDZvhrvvNuv//S+0bWttPGUpLjqOupF1OXLmCBuObrA6HFEESeZClNDkyXDmDNx6K4wcaXU0ZUsplTeK4uLdiy2NRRRNkrkQJfTGG/D22yapK2V1NGXPnsx/3S3tzb2ZJHMhXHD8OLz/vmm5EhQEDz7ov/XkBV3Z8EoAluxegk37WftLPyLJXIhipKRAv35mFMTkZKuj8bzGUY2pV6keKRkprE9eb3U4ohCSzIUowr590K0b/PGHmc8zO9vqiDxPKZV3dy715t5LkrkQhdi0Ca64wixbtzbJvFEjq6OyhjwE9X6SzIVwYvVqM/Xb/v0moS9daiZnDlT2SZ6TDidZHIkojCRzIQrYvh169jQPPa+5Bn7+GapWtToqazWt2pTwkHB2n9jN4dOHrQ5HOCHJXIgCmjSB4cPhjjvMcLYREVZHZL3Q4FC6N+gOwMKdCy2ORjgjyVwIzF34li1mXSmYMAE++ghCQ62Ny5v0bdIXgPnb51sciXBGkrkIeCtWQPv2cO21cOqU2RYc7H+jIF6s65tfD8CsTbNkKjkvJD+uImDZbPDyy9C9O+zda+rF09Ksjsp7Na3alEuqXUJGdgZJh+RBqLeRZC4C0tat0KsXPPkk5OTAo4/Cb78FdosVV/Rs2BOABTsWWByJKEiSuQg4U6dCu3aweDFUrw7z5sHrr0O5clZH5v16NOwBwC+7frE4ElGQJHMRcCpWhLNnYdgw0yGof3+rI/Id/Zv2JyQohGV7l5GSnmJ1OCIfl5K5UmqKUmqlUurpQvZXVkrNV0otUErNUkrJPY7wClrD/Pnw5puObQMGmLvyzz4zd+bCdZXDKtOjQQ9ydI40UfQyxSZzpdRAIFhrfTnQWCnVzEmx24G3tNZ9gcNAP/eGKUTJ2Gzwww9mXJWrr4bHHju/6WGPHtbG58v6NO4DyJC43ibEhTIJwLTc9QVAV2Bb/gJa64n5vqwBBODYcsIbZGTA11/DW2/B+twB/qpWNcm8fn1rY/MXXWK6ALB833KLIxH5uZLMKwAHctePA3GFFVRKXQ5Eaa1XOdk3GhgNEBMTU/JIhShGeroZCMs+TG3duvDIIzBqlKknF+4RXyee8JBw1iev50DaAepWkiZA3sCVOvPTQHjuesXCjlFKVQXeAe5ytl9rPVlrHa+1jq9Ro0ZpYhXiPDk5ZtwUW+58CRERplolPh4++QR27oSHHpJE7m5hIWFc2UiGxPU2riTzREzVCkA7YHfBArkPPKcDT2it97gtOiGc2LQJHn8cYmKgb19YkK/J82efmbHH77hDmhqWpe4xZpyW3/b+ZnEkws6VapbZwG9KqTpAf+BWpdSLWuv8LVtGYKpfnlJKPQVM0lp/4/5wRaA6dgymTYOPP4Y1axzbGzUy1St2MiiWZ3SNMfd3cmfuPYpN5lrrNKVUAtAHeE1rfRj4u0CZScCkMolQCEz1yebNZr1SJRg82Nx9d+kSGJMqe5tOdTtRJawKW1K2sPnYZlpUb2F1SAHPpXbmWutUrfW03EQuRJnZtcu0ROneHXbvdmy/7TbTuefLL+HwYfjgAzN5hCRya4QGh3JTy5sA+DjpY4ujEQBKa+3xk8bHx+s1+f9WFgErKwuWLzcde+bOhQ0bHPvefhsefNC62ETRluxeQsKnCTSJasK2B7ah5H/WMqeUStRaxzvb50qduRBuo7XjbjonBxo2hIMHHfsjI00nnwEDzFJ4r64xXalZoSY7UnewLnkdbWu1tTqkgCbJXJSpzExITDRzaP72m1nfvRvCwsyY4e3bQ+XKpgrlmmtM1Ym0QvENwUHBXHfJdUxJmsL0DdMlmVtMkrlwu3374D//gd9/N69zBeYx+Osv6NzZrE+fDuHhF76H8A2DWw9mStIUZm+ZzfNXPi9VLRaSZC5KTGs4cgT+/tu81q6Fpk1h/HizPyvLPMQEU6XSqpVpjWJ/5e8ALInct3Vv0J1K5SuxPnk9fx/5m9jasVaHFLAkmQuXffghfPONSeBHj56/Ly7OkcwbNoSXXoK2bU3TwSpVPB2p8JSwkDCGtBnCe4nvMW3DNEnmFpJkLrDZzEPIrVvNa9s2s9yyxUzkcNllptzWrbAwd9TTSpXMBA9t25plXL4Re4KC4IknPP85hDVuaXML7yW+x4d/fsjT3Z8mIlR6bllBknmAyMgwDx537TJVH/YJGZKTL+xFmd+GDY5kPnSoudNu1w4aNJA23sLo0aAHHaI7kHgokRkbZzCs3TCrQwpIksx9nNZw8qSpw65XDypUMNs//RRmzIADB8wDyfzVIrGxjmRuH/OsRg245BLzatbs/HW7tm3NS4j8lFKMihtF4txEPvrrI0nmFpFk7oXOnjV3zMnJEBpq7oTBfP3oo4599ldWltm/cKGZpBhM1/c5cxzvGRJi7qYbNTo/IStlelRGRnrmswn/dGubW3l4wcMs3r2YdUfWcWmtS60OKeBIMi8jWpuqjRMnIDXVLO3rqalw552OBPrcc6YH5LFjJjmfOuV4n/79zYTDYOqip0698FyRkVCzpiOpg+n+3rGjGdO7fn2oVcu063ZGErm4WJXDKnNHuzuYtGYSk9ZMYuI1E4s/SLiVJPN8srLgzBmTTM+cgdOnzV1y166OMh9/bKotTp0yr7Q0x+vGG8342WDmmOzZs/Bz9e4NLVua9R07YPVqx77QUJOca9aEJk0c26tWhY8+MonZvr9GDefN+6RKRHjafR3vY9KaSUxdO5VXer9CpfKVrA4poPhcMj9wwNQPnz3reGVkmGVkJFx/vSmXk2PGvE5PN/vT089ff+wxR9kPPoD777+wcwuYu9msLMfDvgkTTNM8Z+zJGUxrj/LlTbO8qCjzqlzZsW6v2wYYOxb++U+TmGvWNOWcPVwMCoLhw0t+zYTwhNY1W5PQMIHFuxfz2d+fcX+n+60OKaD4XDJ/4QV4/33n+2JjHQk6KMh0XLHPQlPQvn2O9dBQk8iDgkySjYw0rwoVzCs725QBk0yPHTOz11SqZF6RkSYB5+8MExdn/oNxRZs2rpUTwtvd1/E+Fu9ezMQ/JnJfx/ukR6gH+Vwyb9jQJO3y5U31QliYY9mokaOcUvD66yYJR0Q4XuHhZtm0qaPskCFwyy3mPYr72fu//3MtTvkZFoHohuY3UCeyDpuObeLX3b/Ss1ERdY3CrWQIXCGEWz2/5HnGLR7HwJYDmTF4htXh+JWihsB1aXIKIYRw1ai4UYQGhTJr0yzWHJSbNk+RZC6EcKvoyGju63gfGs2Y+WOw4q//QCTJXAjhduMSxhEVFsXK/StZsmeJ1eEEBEnmQgi3qxJWhQc7mzn/Xl72ssXRBAZJ5kKIMnF/p/upEFqBBTsWsGzvMqvD8XuSzIUQZaJqeFUe6my6RD+16CmLo/F/ksyFEGVmbJexVAitwNI9S0k6lGR1OH5NkrkQosxElo9kdIfRAIz4fgRns13sFi1KTJK5EKJMjesxjpjKMSQdTuKl316yOhy/JclcCFGmKodV5rMBnwHwwtIXmLNlTjFHiNKQZC6EKHM9Gvbg6W5PA3DrjFtZtGuRxRH5H0nmQgiPeP7K57mj3R2kZ6XT67NeTFg1weqQ/IokcyGERyil+PD6D/Pu0B9b+Bi/7vrV4qj8hyRzIYTHhASF8ELPF7g3/l4yczLpM7UPb6x4gxxbjtWh+TxJ5kIIj3vn6nd4uPPD5Ogc/vXzv+j7eV/2ndxX/IGiUJLMhRAeF6SCePOqN/n+1u+pEVGDRbsWcemkS3n393c5cfaE1eH5JEnmQgjLXNf8Otbds45rL7mWk5knuX/+/dR4vQYDvxnI3K1zpfqlBGSmISGE5bTWTF07lY+SPmLpnqVoTF6qG1mXQa0GcXWzq+nWoBthIWEWR2qtomYacimZK6WmAK2AuVrrF0tbxk6SuRCiMAfSDjB17VQ+/PNDdqTuyNseEhRC21ptaVG9BfUr1adOZB3qVapH/Ur1qV+5PtUjqhMS5HPTGpfIRSVzpdRA4Hqt9Z1KqY+Al7XW20paJj9J5kKI4mitWb5vOXO3zmXe9nmsO7Iu747dGYWiTmQdWtZoSWS5SCqWq0hkuUiqhFUhIjSCsJAwwkLCCA8NN8uQ8Au2hYWEUS64HEEqCIUyS6XO+9rZNvvXRW1Tbpjl/WKT+X+BH7XW85RStwLhWuuPS1omP0nmQoiSOn3uNEmHktiRuoP9afs5eOog+9P2s/fkXg6cOkBKekqRyd4bKBSd63VmxYgVpTu+iGTuyt8kFYADuevHgbjSlFFKjQZG5355Wim1pUCR6sAxF+IJNHJdLiTX5EJyTZzzquui0axkJWpkqe/SGxS2w5VkfhoIz12viPMWMMWW0VpPBiYXdhKl1JrC/scJZHJdLiTX5EJyTZwLpOviStPERKBr7no7YHcpywghhCgjrtyZzwZ+U0rVAfoDtyqlXtRaP11Emc7uD1UIIURhir0z11qnAQnAKuBKrfXfBRK5szInSxFLoVUwAU6uy4XkmlxIrolzAXNdLOk0JIQQwr2kO78QQvgBSeZCCOEH3Nb3VSm1sIj326+1Huquc/kKuSbOyXVxr5IMpeHrlFKVga+BYOAMcAswiQKf39k1cXWbz9Jau+UF9C5i34B86xOB6/J9PQVYCTztjm3e9CrBNakFJAXCNXHlugCVgfnAAmAWUC4Qrkspr+VA4JPc9Y+AZlbHVMaf916gT+76JOAfBT+/s2vi6jarP9/FvDxazaKU6gbU1lrPyf16IBCstb4caKyUanYx2zz5WdzsDXI7Xck1AeB24C2tdV/gMNBPrkuhEoBpuesLcPT38Eta64la659zv6wBDOXCz59wEdt8lseSuVIqFPgA2K2UuiF3cwIB/o1QSvXE/Ll4OHdTAgF+TZz8wiYj16UwBYfSqGVhLB6jlLociAL2ceHnd3ZNXN3mszx5Z/4PYCPwGtBJKfUAF3fRff4boZQqBzwDPJ5vc0Bfk/zsv7Ba61XIdSmMK8Nt+BWlVFXgHeAunH/+i9nmszwZfHtgstb6MPA5cCXyjXgcmKi1zj9PVqBfE+CCX1iQ61KYgBpKI/cGaDrwhNZ6D84//8Vs81meHMl9O9A4dz0e2AMkYS7mKszF3ALsv4htvqY30FMpdR8Qq5T6EFhKYF8TZ7+w4PjFC9jrUohAG0pjBGZU1qeUUk8BHwPDCnx+zYXXxNVtvsuNT5mLa6EQifkFXYppVVAXqAT8DbwFbMK0Yij1NqufJpf0mhT4enHu0q+viYs/K/cAqcDi3NctgXBdLuJ6RgGDMY0LLI/HGz7/xWzz1ZfbuvMrpaZjHlY585fW+sFCjosC+gBLtamCuaht3kSuiXNyXYRwPxmbRQgh/IA/PQgSQoiAJclcCCH8gCRzYTml1J1KKV3Iq6F9aVFsbju/UmqxUmq8C+XuVErtvtjzicDiyaaJQhRmDtAxd/174AcckwqUsyQiIXyMJHNhOa11CpACoJQ6BxzUWq/J/bqhdZEJ4TukmkUIIfyAJHPhK6oopb5VSp1WSm3OHbcFcNQxK6XClVJvK6UOKqV65NsfqZT6QCmVqpQ6rJR6K7eXqX1/XaXU7Nz9x5RSHymlwkpw/iil1Oe5+w4rpcYppZSrH0wp1UoptVwpdVYptRJoVLpLJAKZJHPhK6YChzA9RM9gxsUvaCbQBngZM3yE3QdAd2AY8EDu8vl8+ycBTTE9Ae8G+gKPlOD8X2Pq/IcA44BHOX/wtEIppUIwXfIVcAMwD3jClWOFyE/qzIWv2KS1fgBAKfUSJoHm1wAz9sptOl9POKVUY8xwAAla6yW521oBI3Ek3BhMz9Cfc/fvwIzbUez5lVJdMcm/vdb6r9xtEcALSqm3tNaZxXyuvpiJEvprrXcAPyml2mPGHxHCZZLMha+YnG89hQt/ds8Bj+gLuzS3zl0uLljzoZQqp7U+B7wPvJM7acUq4Aet9WoXzx8LnLQncvu5MMPuNgU2FPO5mgHHcxO53VIkmYsSkmoW4St2FbP/kNb6gJPt9gzeBTMMc/5XNoDWehLQFpiLGVVxpVKq4PgwRZ2/4H8g9q9dqTcPAmwFtuW4cJwQ55FkLnxFaROc/c44SGv9V+4ddDjwMLl310qpVzDjFP1Ha3095i78rgLvU9j5/8I8HG2bb1sPIB3Y5kJ8O4BqSqmYfNu6uHCcEOeRahbh17TWO5RS3wBTlFJPABmYOVd351axAHQC4nOTeghmmjmXxjzXWi9TSv0MfKOUGgvUxjxcfdGF+nKAHzFj+09VSr0IdABuwjEzkhAukTtzEQhGAb8BU4AvgDWYFi12dwJpwDeYMfe3A/eX4P1vAf4EvgJeBN4GXnXlwNz/UPpjqlpmA4OAN0twbiEAGQJXCCH8gtyZCyGEH5BkLoQQfkCSuRBC+AFJ5kII4QckmQshhB+QZC6EEH5AkrkQQvgBSeZCCOEH/h+VavHDKx4mogAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 自定义阀值调整函数\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')  # 忽视警告\n",
    "def plot_precision_recall_vs_threshold(y_true, y_scores):\n",
    "    precisions, recalls, thresholds = precision_recall_curve(y_true, y_scores)\n",
    "    plt.plot(thresholds, precisions[:-1], \"b--\", label=\"Precision\", linewidth=2)\n",
    "    plt.plot(thresholds, recalls[:-1], \"g-\", label=\"Recall\", linewidth=2)\n",
    "    plt.xlabel(\"Threshold\", fontsize=16)\n",
    "    plt.legend(loc=\"upper left\", fontsize=16)\n",
    "    plt.ylim([0, 1])\n",
    "    plt.show()\n",
    "\n",
    "plot_precision_recall_vs_threshold(y_true=y_train_9, y_scores=y_scores_sgd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9132553606237817\n",
      "0.15750546310304253\n"
     ]
    }
   ],
   "source": [
    "# 根据上图显示可知，将阀值设置成9000时，精度达到一个峰值\n",
    "y_train_pred_90 = (y_scores_sgd > 9000)\n",
    "p_score = precision_score(y_train_9, y_train_pred_90)\n",
    "r_score = recall_score(y_train_9, y_train_pred_90)\n",
    "print(p_score)\n",
    "print(r_score)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 小结2\n",
    "  * F1分数是精度和召回率的均衡评价指标\n",
    "  * 精度和召回率并不是正比例关系，有时精度高，召回率就低，根据具体业务来选择提高精度还是召回率\n",
    "  * 可通过调整阀值来权衡精度和召回率，可从上述结果看出，当阀值设置为9000时，精度提升到90%以上，但是召回率低到15%左右"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ROC曲线和AUC分数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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FnY1Z2ZmMnnyZO677z7q1asHWNPqzz77bGbMmIEUGWPduXMnH3/8MUuWLOH8888HYNOmTbz11luFSWPPnj0MGDCAIUOGANCuXbvCY7Rp04Y2bdowb948tm7dekJsdnFE0ogOCw7o682YMYMJEyYQEhJCcnIyPXr0YPr06QGNQakap4Lf8GuDdevWERUVdcLY5MCBA8nIyGD79u107twZsMZBXnzxxRMSBsDGjRsLn1Ncbm4uISEh3Hrrrdx5552F0/Ivvvhi+vbtW31v6hQ54mtx8X+o6pKSkkL//v255ppryMnJ4ZZbbtGWhVIOV/z8UvB70XGNpk2b0rx585OeW7DPypUrWbt27Qm3oCDrO/ttt93Ghg0bGDlyJOvXr6dfv368/PLL1fV2TpkjzniBSBmLFi2iSZMmfP/997Rv356tW7fy/PPPB+CVlVJ26dGjBykpKWzYsKFw29KlSwkPD/erXlxBS8Tr9dKjRw969OhBVlYWL730Eh6PdfG4hx9+GGMM99xzD3PmzGHixIm88847JxwnNDSUrKysKnxnleeIpBGIL/vdu3cnPDycp59+mm3bttGuXbvqf1GllK3OPfdchgwZwtixY5k7dy5vvvkmf/3rX3n88ccLxzPK0q5dO8aOHcuECROYNWsWCxYs4OabbyYpKalwau/PP//M3XffzTfffMPChQtZsmTJSQPdZ555JmvXrmXWrFksW7aMV199tVrer1+MMbX61rupy3zwziumOjz11FOmUaNG5ujRo9VyfKVqq02bNtkdQpX77rvvjHVKPFFycrK55pprTEREhImLizNTpkwx+fn5hY//97//Na1bty71uKmpqWbChAkmOjraxMTEmHHjxpmkpKTCx3fv3m1Gjx5tGjZsaCIjI83IkSPNnj17TjrOE088YeLi4kxwcLAZMWJEue+nvH8jYLWpxDlXTJF+udqoTzO3uf/pl/m/G+6ssmPu2LGDoUOHsmPHDkJCQliwYAGDBw+usuMrVdtt3ryZTp062R2GKkN5/0YissYYU+HpWM7onqrCgfD777+fDh06sGPHDvr378/hw4c1YSillI8jkkZVzZ7yeDxMmzaN0NBQPvroI1asWKEFBpVSqghHJI1gd+WThtfrZeLEiWzZsoWgoCC+//57kpOTGTt2bBVGqJRSzuCIxX2V7Z5asWIFo0aNIjk5mR07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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SGDAUC:0.9599354250386889\n",
      "随机森林AUC：0.9862674133003122\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import roc_curve\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.metrics import roc_auc_score\n",
    "\n",
    "# 自定义ROC曲线函数\n",
    "def plot_roc_curve(y_true, y_scores, label):\n",
    "    fpr, tpr, thresholds = roc_curve(y_true, y_scores)\n",
    "    plt.plot(fpr, tpr, linewidth=2, label=label)\n",
    "    plt.plot([0, 1], [0, 1], 'k--')\n",
    "    plt.axis([0, 1, 0, 1])\n",
    "    plt.xlabel('假正类率', fontsize=16)\n",
    "    plt.ylabel('真正类率', fontsize=16)\n",
    "\n",
    "# 比较随机森林和SGD的ROC曲线\n",
    "rf_clf = RandomForestClassifier(n_estimators=10, random_state=42)\n",
    "y_proda_rf = cross_val_predict(rf_clf, x_train, y_train_9, cv=3, method='predict_proba')\n",
    "y_scores_rf = y_proda_rf[:, 1]\n",
    "plot_roc_curve(y_true=y_train_9, y_scores=y_scores_sgd, label='SGD')\n",
    "plot_roc_curve(y_true=y_train_9, y_scores=y_scores_rf, label='forest')\n",
    "plt.legend(loc=\"lower right\", fontsize=16)\n",
    "plt.show()\n",
    "# 获得随机森林和SGD的AUC分数\n",
    "auc_score_sgd = roc_auc_score(y_train_9, y_scores_sgd)\n",
    "auc_score_rf = roc_auc_score(y_train_9, y_scores_rf)\n",
    "print('SGDAUC:{}'.format(auc_score_sgd))\n",
    "print('随机森林AUC：{}'.format(auc_score_rf))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 小结3\n",
    "  * 通过合适的指标（ROC曲线）利用交叉验证来评估各种分类器\n",
    "  * 选择满足需求的精度和召回率权衡"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 多类别分类器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### OvA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[3]\n",
      "[[-47579.686651   -26323.25842842 -16923.9555781   -2222.40356584\n",
      "   -8304.30825986  -3686.0216797  -19145.32988739 -11326.19710087\n",
      "  -10402.0386515   -2528.6175673 ]]\n",
      "[0 1 2 3 4 5 6 7 8 9]\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "# SGD从二元分类到多类别分类\n",
    "sgd_clf.fit(x_train, y_train)\n",
    "a = sgd_clf.predict([some_digit])\n",
    "print(a)\n",
    "some_digit_scores = sgd_clf.decision_function([some_digit])\n",
    "print(some_digit_scores)\n",
    "c = sgd_clf.classes_\n",
    "print(c)\n",
    "print(c[np.argmax(some_digit_scores)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### OvO"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[4]\n",
      "OVO的分类器个数：45\n"
     ]
    }
   ],
   "source": [
    "from sklearn.multiclass import OneVsOneClassifier\n",
    "ovo_clf = OneVsOneClassifier(SGDClassifier(max_iter=5, tol=-np.infty, random_state=42))\n",
    "ovo_clf.fit(x_train, y_train)\n",
    "print(ovo_clf.predict([some_digit]))\n",
    "print('OVO的分类器个数：{}'.format(len(ovo_clf.estimators_)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[9]\n",
      "[[0.  0.  0.  0.  0.1 0.  0.  0.  0.  0.9]]\n"
     ]
    }
   ],
   "source": [
    "# 随机森林直接将实例分为多个类别，调用predict_proba()可以获得分类器将每个实例分类为每个类别的概率列表\n",
    "rf_clf.fit(x_train, y_train)\n",
    "print(rf_clf.predict([some_digit]))\n",
    "print(rf_clf.predict_proba([some_digit]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 性能评估"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 准确率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.8678  0.88195 0.86965] 标准化后：[0.90425 0.9031  0.8903 ]\n"
     ]
    }
   ],
   "source": [
    "from sklearn.preprocessing import StandardScaler\n",
    "# 比较一下数据标准化前后的准确率\n",
    "accuracy1 = cross_val_score(sgd_clf, x_train, y_train, cv=3, scoring='accuracy')\n",
    "scaler = StandardScaler()\n",
    "x_train_scaled = scaler.fit_transform(x_train)\n",
    "accuracy2 = cross_val_score(sgd_clf, x_train_scaled, y_train, cv=3, scoring='accuracy')\n",
    "print('{} 标准化后：{}'.format(accuracy1, accuracy2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 混淆矩阵图（错误分析）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 自定义混淆矩阵图函数\n",
    "def plot_conf_matrix(conf_mx, error_analysis=False):\n",
    "    if error_analysis is True:\n",
    "        row_sums = conf_mx.sum(axis=1, keepdims=True)\n",
    "        norm_conf_mx = conf_mx / row_sums\n",
    "        np.fill_diagonal(norm_conf_mx, 0)\n",
    "        plt.matshow(norm_conf_mx, cmap=plt.get_cmap('jet'))\n",
    "        plt.show()\n",
    "    elif error_analysis is False:\n",
    "        plt.matshow(conf_mx, cmap=plt.get_cmap('jet'))\n",
    "        plt.show()\n",
    "    else:\n",
    "        raise TypeError('Abnormal format: error_analysis')\n",
    "\n",
    "y_train_pred = cross_val_predict(sgd_clf, x_train_scaled, y_train, cv=3)\n",
    "conf_ma_sgd = confusion_matrix(y_train, y_train_pred)\n",
    "plot_conf_matrix(conf_mx=conf_ma_sgd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 错误分析\n",
    "plot_conf_matrix(conf_mx=conf_ma_sgd, error_analysis=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 小结4\n",
    "  * 每行代表实际类别，每列代表预测类别\n",
    "  * 竖着看，8和9最亮，说明很多图片被误认为8和9\n",
    "  * 横着看，3、5、8和9比较亮，说明这些数字图片还有很多没有被认出来\n",
    "  * 如何优化分类器，从数据上，增加具有代表性的训练数据；从特征上，加入更多的新特征；从参数上，参数调优；从算法上，优化算法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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VKgAmOukSqJ3lSlTzdNRo0bJOqLmqgqKXLx4MRUrVnTJ9e1xSbSq+o+oxNxXXnkFgO7du8t7GjRoQPny5cX3525UmkTbtm1ZsWKFHO/Tpw9TpkwBPGtWhahk5B9//JEaNWqwbds2AIla9QSzZs0S3+c///xjc65QoUKymBYuXFgqzribU6dO8c477wCmedo6Ito6WvX48eNSECCePNfRqtb0799fFs/evXvz2Wefecx02bp1a5YvXy6vrb9XFV0bGBhoUxnFmpo1a1KmTBmJsPVUUvvff/9NxYoV5fonT560SXtxJ4sXL6Zt27aAmca2evVqAHLlyuXWcRiGweTJkwEk4l1Ru3ZtfvjhB1eOy/F8Ngwjtp8EUadOHaNOnTpGsmTJjGTJkhkWi8WwWCzyWv00btzYuHr1qnH16tWEXirR3Lt3z2jRooWM0WKxGD/88IPxww8/eGxMivDwcCM8PNzAXNSMoUOHGkOHDvXYeDZu3GikTp3a5l6VK1fOKFeunPHjjz8aAQEBRvXq1Y3q1asbadOmNTZt2mRs2rTJrWP09/c3atSoIffMYrFE+12NvV+/fgm9zLPmTVL9iTd37tyR71Tdt/Tp0xvp06c3+vXrZxw4cMA4cOCA8ejRo4R8fLx49913bdaPmNYVR8esj9erV8+oV6+eERIS4vIxO+L77783ChcubPzxxx/GH3/84ZExGIZhbN261XjllVcMb29vw9vb29i4caPHxrJ+/XqZp4CRIUMGI0OGDEb79u2Nhw8fuvryDueL5+2HGo1Go9EkMVxi01R2akdhvQcPHpR/N23axHvvvQeYIfeeIE2aNCxZskSitIKDg+nbty8ADx488GiBceWXLVeuHP7+/hLG3qFDB0fFc91CuXLlpEbhe++9J4nyKkFZpU1EREQwadIkwDShuxplwm3SpAkhISFiOhs2bBjVq1cHTPNV+/btxayq/h+amLEO59++fTuHDx+WwuNff/21mFzffPNNGjRoQK1atQD31/WND8o9sXDhQgYOHOj260+aNIl69epRt25dt1/bmh49ehAWFsbYsWMBs4CIp1D+V4WqKduvXz9PDAeIp89x6dKlAFy7do1evXolyl+4adMmxo8fLwEd33//vdi+PeGHVEL7zTffFCdwxYoV8fPz83gwzMOHD2nRogW///47YDrN58+f79ExxYTy31qH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\n",
      "text/plain": [
       "<Figure size 576x576 with 4 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_digits(instances, images_per_row=10, **options):\n",
    "    size = 28\n",
    "    images_per_row = min(len(instances), images_per_row)\n",
    "    images = [instance.reshape(size,size) for instance in instances]\n",
    "    n_rows = (len(instances) - 1) // images_per_row + 1\n",
    "    row_images = []\n",
    "    n_empty = n_rows * images_per_row - len(instances)\n",
    "    images.append(np.zeros((size, size * n_empty)))\n",
    "    for row in range(n_rows):\n",
    "        rimages = images[row * images_per_row : (row + 1) * images_per_row]\n",
    "        row_images.append(np.concatenate(rimages, axis=1))\n",
    "    image = np.concatenate(row_images, axis=0)\n",
    "    plt.imshow(image, cmap = matplotlib.cm.binary, **options)\n",
    "    plt.axis(\"off\")\n",
    "\n",
    "\n",
    "# 查看数字3和数字5的例子\n",
    "cl_a, cl_b = 3, 5\n",
    "x_aa = x_train[(y_train == cl_a) & (y_train_pred == cl_a)]\n",
    "x_ab = x_train[(y_train == cl_a) & (y_train_pred == cl_b)]\n",
    "x_ba = x_train[(y_train == cl_b) & (y_train_pred == cl_a)]\n",
    "x_bb = x_train[(y_train == cl_b) & (y_train_pred == cl_b)]\n",
    "\n",
    "plt.figure(figsize=(8,8))\n",
    "plt.subplot(221); \n",
    "plot_digits(x_aa[:25], images_per_row=5)\n",
    "plt.subplot(222); \n",
    "plot_digits(x_ab[:25], images_per_row=5)\n",
    "plt.subplot(223);\n",
    "plot_digits(x_ba[:25], images_per_row=5)\n",
    "plt.subplot(224); \n",
    "plot_digits(x_bb[:25], images_per_row=5)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 小结5\n",
    "  * 通过上面图像，如果书写3 的连接点左移，分类器可能将其分类为数字5，这个分类器对图像位移和旋转敏感\n",
    "  * 减少混淆的方法之一，就是对图像进行预处理，确保位于中心位置并且没有旋转"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 多标签分类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ True  True]]\n"
     ]
    }
   ],
   "source": [
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "# K近邻分类可实现多标签分类\n",
    "knn = KNeighborsClassifier()\n",
    "y_1 = (y_train > 7)\n",
    "y_2 = (y_train % 2 == 1)\n",
    "y_multi = np.c_[y_1, y_2]\n",
    "knn.fit(x_train, y_multi)\n",
    "print(knn.predict([some_digit]))\n",
    "# 样本数据是9 符合大于7和奇数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 多输出分类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 对原图像增加噪声\n",
    "noise = np.random.randint(0, 100, (len(x_train), 784))\n",
    "x_train_mod = x_train + noise\n",
    "y_train_mod = x_train\n",
    "some_index = 5500\n",
    "plt.subplot(121)\n",
    "plt.imshow(x_train_mod[some_index].reshape(28, 28), cmap = matplotlib.cm.binary)\n",
    "plt.subplot(122)\n",
    "plt.imshow(y_train_mod[some_index].reshape(28, 28), cmap = matplotlib.cm.binary)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 用KNN来实现多输出分类\n",
    "knn.fit(x_train_mod, y_train_mod)\n",
    "y_pred = knn.predict([x_train_mod[some_index]])\n",
    "plt.imshow(y_pred.reshape(28, 28), cmap = matplotlib.cm.binary)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 作业二"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'mean_fit_time': array([ 38.30898051, 104.43142438, 162.81468072,  58.47921839,\n",
      "       163.49601479, 260.0655273 ,  67.94814644, 185.47756286,\n",
      "       195.94949193]), 'std_fit_time': array([ 0.95721514,  1.32840103,  2.78536916,  1.3582995 ,  2.1173883 ,\n",
      "        2.31894619,  0.8031986 , 10.86233099, 26.07734866]), 'mean_score_time': array([0.72489991, 1.85893664, 2.74307671, 0.95708685, 2.51489639,\n",
      "       4.10849733, 1.08108187, 2.11920199, 2.1450799 ]), 'std_score_time': array([0.06048432, 0.0745896 , 0.08477751, 0.01509771, 0.03968021,\n",
      "       0.30077759, 0.06216043, 0.35689833, 0.2648795 ]), 'param_max_depth': masked_array(data=[5, 5, 5, 10, 10, 10, 15, 15, 15],\n",
      "             mask=[False, False, False, False, False, False, False, False,\n",
      "                   False],\n",
      "       fill_value='?',\n",
      "            dtype=object), 'param_n_estimators': masked_array(data=[100, 300, 500, 100, 300, 500, 100, 300, 500],\n",
      "             mask=[False, False, False, False, False, False, False, False,\n",
      "                   False],\n",
      "       fill_value='?',\n",
      "            dtype=object), 'params': [{'max_depth': 5, 'n_estimators': 100}, {'max_depth': 5, 'n_estimators': 300}, {'max_depth': 5, 'n_estimators': 500}, {'max_depth': 10, 'n_estimators': 100}, {'max_depth': 10, 'n_estimators': 300}, {'max_depth': 10, 'n_estimators': 500}, {'max_depth': 15, 'n_estimators': 100}, {'max_depth': 15, 'n_estimators': 300}, {'max_depth': 15, 'n_estimators': 500}], 'split0_test_score': array([0.85675   , 0.85991667, 0.86041667, 0.94625   , 0.94725   ,\n",
      "       0.94841667, 0.96558333, 0.96658333, 0.96708333]), 'split1_test_score': array([0.86091667, 0.86541667, 0.86533333, 0.94616667, 0.94816667,\n",
      "       0.94808333, 0.96416667, 0.96625   , 0.96708333]), 'split2_test_score': array([0.85383333, 0.8585    , 0.86175   , 0.9425    , 0.94525   ,\n",
      "       0.94608333, 0.96316667, 0.96575   , 0.96625   ]), 'split3_test_score': array([0.85425   , 0.86033333, 0.86125   , 0.94533333, 0.94475   ,\n",
      "       0.94583333, 0.96325   , 0.9645    , 0.964     ]), 'split4_test_score': array([0.855     , 0.85733333, 0.858     , 0.94183333, 0.94475   ,\n",
      "       0.94508333, 0.96275   , 0.96333333, 0.96433333]), 'mean_test_score': array([0.85615   , 0.8603    , 0.86135   , 0.94441667, 0.94603333,\n",
      "       0.9467    , 0.96378333, 0.96528333, 0.96575   ]), 'std_test_score': array([0.00258382, 0.00277018, 0.00237194, 0.00187676, 0.00140989,\n",
      "       0.00131191, 0.00101187, 0.00120485, 0.00133229]), 'rank_test_score': array([9, 8, 7, 6, 5, 4, 3, 2, 1])}\n",
      "{'max_depth': 15, 'n_estimators': 500}\n"
     ]
    }
   ],
   "source": [
    "# 模型选择\n",
    "# knn_clf = KNeighborsClassifier()\n",
    "# 网格参数设置\n",
    "# param_grid = [\n",
    "#     {'n_neighbors': [4, 5, 6], 'weights': ['uniform', 'distance']}\n",
    "# ]\n",
    "# grid_search = GridSearchCV(estimator=knn_clf, param_grid=param_grid, n_jobs=-1,\n",
    "#                            scoring='precision_micro', return_train_score=True)\n",
    "# grid_search.fit(x_train, y_train)\n",
    "# print(grid_search.cv_results_)\n",
    "# print(grid_search.best_params_)\n",
    "# 交叉验证\n",
    "# y_train_9_pred = cross_val_predict(knn_clf, x_train, y_train_9, cv=3, n_jobs=-1)\n",
    "# knn_precision = precision_score(y_train_9, y_train_9_pred)\n",
    "# print(knn_precision)\n",
    "# knn_clf.fit(x_train, y_train)\n",
    "# y_test_pred = knn_clf.predict(x_test)\n",
    "# precision = precision_score(y_test, y_test_pred, average='micro')\n",
    "# print(precision)\n",
    "# 用网格搜索KNN的参数，亲测训练时间过长，超过半小时，原因在于KNN 为“惰性算法”\n",
    "# 在拟合过程中它除了保存输入数据外什么也不做，特别是根本没有学习。在预测期间，将对每个测试数据点进行实际距离计算。\n",
    "# 因此，在使用GridSearchCV和cross_val_predict，KNN必须预测验证数据点，这会使计算时间更长。所以，本次采用随机森林分类器。\n",
    "from sklearn.model_selection import GridSearchCV\n",
    "rf_clf = RandomForestClassifier(random_state=42)\n",
    "param_grid = [\n",
    "    {'n_estimators': [100, 300, 500], 'max_depth': [5, 10, 15]}\n",
    "]\n",
    "grid_search = GridSearchCV(estimator=rf_clf, param_grid=param_grid, n_jobs=-1, scoring='precision_micro')\n",
    "grid_search.fit(x_train, y_train)\n",
    "print(grid_search.cv_results_)\n",
    "print(grid_search.best_params_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None,\n",
       "                       criterion='gini', max_depth=15, max_features='auto',\n",
       "                       max_leaf_nodes=None, max_samples=None,\n",
       "                       min_impurity_decrease=0.0, min_impurity_split=None,\n",
       "                       min_samples_leaf=1, min_samples_split=2,\n",
       "                       min_weight_fraction_leaf=0.0, n_estimators=500,\n",
       "                       n_jobs=None, oob_score=False, random_state=42, verbose=0,\n",
       "                       warm_start=False)"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 网格搜索后的最优模型参数\n",
    "rf_clf_final = grid_search.best_estimator_\n",
    "rf_clf_final"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 预测测试集数据\n",
    "y_test_pred = rf_clf_final.predict(x_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9678"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 计算最优模型精度\n",
    "precision = precision_score(y_test, y_test_pred, average='micro')\n",
    "precision"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 作业三"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(300000, 784)\n",
      "(300000,)\n"
     ]
    }
   ],
   "source": [
    "from sklearn.base import BaseEstimator, TransformerMixin\n",
    "from sklearn.pipeline import Pipeline, FeatureUnion\n",
    "\n",
    "# 自定义平移转换类\n",
    "class ImageMove(BaseEstimator, TransformerMixin):\n",
    "    \n",
    "    def __init__(self, delta_v=0, delta_h=0):\n",
    "        self.v = delta_v\n",
    "        self.h = delta_h\n",
    "\n",
    "    def fit(self, x, y=None):\n",
    "        return self\n",
    "\n",
    "    def transform(self, x, y=None):\n",
    "        is_m = []\n",
    "        for im in x:\n",
    "            im = im.reshape(28, 28)\n",
    "            rows = im.shape[0]\n",
    "            cols = im.shape[1]\n",
    "            i_m = np.zeros(shape=(rows, cols), dtype=np.uint8)\n",
    "            move_matrix = np.array([[1, 0, self.v], [0, 1, self.h], [0, 0, 1]])\n",
    "            for i in range(rows):\n",
    "                for j in range(cols):\n",
    "                    cursor = np.array([i, j, 1])\n",
    "                    [xm, ym, zm] = np.dot(move_matrix, cursor)\n",
    "                    xm = int(xm)\n",
    "                    ym = int(ym)\n",
    "                    if xm >= rows or ym >= cols or xm < 0 or ym < 0:\n",
    "                        i_m[i][j] = 0\n",
    "                    else:\n",
    "                        i_m[i][j] = im[xm][ym]\n",
    "            i_m = i_m.reshape(-1)\n",
    "            is_m.append(i_m)\n",
    "        return np.array(is_m)\n",
    "\n",
    "# 不动数据\n",
    "origin_pipeline = Pipeline([\n",
    "    ('no_move', ImageMove())\n",
    "])\n",
    "# 左平移一个像素数据\n",
    "left_pipeline = Pipeline([\n",
    "    ('left_move', ImageMove(delta_h=1))\n",
    "])\n",
    "# 右平移一个像素数据\n",
    "right_pipeline = Pipeline([\n",
    "    ('right_move', ImageMove(delta_h=-1))\n",
    "])\n",
    "# 上平移一个像素数据\n",
    "up_pipeline = Pipeline([\n",
    "    ('up_move', ImageMove(delta_v=1))\n",
    "])\n",
    "# 下平移一个像素数据\n",
    "down_pipeline = Pipeline([\n",
    "    ('down_move', ImageMove(delta_v=-1))\n",
    "])\n",
    "# 数据整合\n",
    "preprocess_pipeline = FeatureUnion([\n",
    "    ('origin_pipeline', origin_pipeline),\n",
    "    ('left_pipeline', left_pipeline),\n",
    "    ('right_pipeline', right_pipeline),\n",
    "    ('up_pipeline', up_pipeline),\n",
    "    ('down_pipeline', down_pipeline)\n",
    "])\n",
    "# 特征数据增广\n",
    "x_train_aug = preprocess_pipeline.fit_transform(x_train)\n",
    "x_train_aug = x_train_aug.reshape(-1, 784)\n",
    "print(x_train_aug.shape)\n",
    "# 标签数据增广\n",
    "sy_train = y_train.reshape(-1, 1)\n",
    "sy_train_aug = np.c_[sy_train, sy_train, sy_train, sy_train, sy_train].reshape(-1)\n",
    "print(sy_train_aug.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None,\n",
       "                       criterion='gini', max_depth=15, max_features='auto',\n",
       "                       max_leaf_nodes=None, max_samples=None,\n",
       "                       min_impurity_decrease=0.0, min_impurity_split=None,\n",
       "                       min_samples_leaf=1, min_samples_split=2,\n",
       "                       min_weight_fraction_leaf=0.0, n_estimators=500,\n",
       "                       n_jobs=None, oob_score=False, random_state=42, verbose=0,\n",
       "                       warm_start=False)"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 最优模型训练\n",
    "rf_clf_final.fit(x_train_aug, sy_train_aug)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9736\n"
     ]
    }
   ],
   "source": [
    "# 计算通过数据增广后模型的精度\n",
    "y_test_pred_aug = rf_clf_final.predict(x_test)\n",
    "precision_aug = precision_score(y_test, y_test_pred_aug, average='micro')\n",
    "print(precision_aug)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 小结6\n",
    "  * 对比数据增广前后的精度0.9678和0.9736，可以看出，通过数据增广，模型精度提高了，达到了优化精度的目的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 垃圾邮件分类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 读取邮件数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import re\n",
    "import nltk\n",
    "import numpy as np\n",
    "import email.policy\n",
    "from email.parser import BytesParser\n",
    "from collections import Counter\n",
    "from html import unescape\n",
    "from urlextract import URLExtract\n",
    "\n",
    "# 明确工作路径\n",
    "d = os.path.dirname('.')\n",
    "run_dir = os.path.abspath(d)\n",
    "os.chdir(run_dir)\n",
    "# 自定义邮件地址\n",
    "email_data_ham_path = os.path.join(run_dir, 'datasets/spam/easy_ham')\n",
    "email_data_spam_path = os.path.join(run_dir, 'datasets/spam/spam')\n",
    "ham_filesnames = [name for name in sorted(os.listdir(email_data_ham_path)) if len(name) > 20]\n",
    "spam_filesnames = [name for name in sorted(os.listdir(email_data_spam_path)) if len(name) > 20]\n",
    "\n",
    "# 自定义读取邮件数据函数\n",
    "def load_email_data(data_path, data_name):\n",
    "    email_data_name = os.path.join(data_path, data_name)\n",
    "    with open(email_data_name, 'rb') as f:\n",
    "        return BytesParser(policy=email.policy.default).parse(f)\n",
    "\n",
    "# 读取邮件数据\n",
    "ham_data = [load_email_data(data_path=email_data_ham_path, data_name=name) for name in ham_filesnames]\n",
    "spam_data = [load_email_data(data_path=email_data_spam_path, data_name=name) for name in spam_filesnames]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Counter({'text/plain': 2408, 'multipart/signed': 68, 'multipart/mixed': 10, 'multipart/alternative': 9, 'multipart/related': 3, 'multipart/report': 2})\n",
      "Return-Path : <12a1mailbot1@web.de>\n",
      "Delivered-To : zzzz@localhost.spamassassin.taint.org\n",
      "Received : from localhost (localhost [127.0.0.1])\tby phobos.labs.spamassassin.taint.org (Postfix) with ESMTP id 136B943C32\tfor <zzzz@localhost>; Thu, 22 Aug 2002 08:17:21 -0400 (EDT)\n",
      "Received : from mail.webnote.net [193.120.211.219]\tby localhost with POP3 (fetchmail-5.9.0)\tfor zzzz@localhost (single-drop); Thu, 22 Aug 2002 13:17:21 +0100 (IST)\n",
      "Received : from dd_it7 ([210.97.77.167])\tby webnote.net (8.9.3/8.9.3) with ESMTP id NAA04623\tfor <zzzz@spamassassin.taint.org>; Thu, 22 Aug 2002 13:09:41 +0100\n",
      "From : 12a1mailbot1@web.de\n",
      "Received : from r-smtp.korea.com - 203.122.2.197 by dd_it7  with Microsoft SMTPSVC(5.5.1775.675.6);\t Sat, 24 Aug 2002 09:42:10 +0900\n",
      "To : dcek1a1@netsgo.com\n",
      "Subject : Life Insurance - Why Pay More?\n",
      "Date : Wed, 21 Aug 2002 20:31:57 -1600\n",
      "MIME-Version : 1.0\n",
      "Message-ID : <0103c1042001882DD_IT7@dd_it7>\n",
      "Content-Type : text/html; charset=\"iso-8859-1\"\n",
      "Content-Transfer-Encoding : quoted-printable\n"
     ]
    }
   ],
   "source": [
    "# 获取email结构\n",
    "def get_email_structure(email_data):\n",
    "    if isinstance(email_data, str):\n",
    "        return email_data\n",
    "    payload = email_data.get_payload()\n",
    "    if isinstance(email_data, list):\n",
    "        return 'multipart({})'.format(', '.join([get_email_structure(i) for i in payload]))\n",
    "    else:\n",
    "        return email_data.get_content_type()\n",
    "\n",
    "def structures_counter(emails_data):\n",
    "    result = Counter()\n",
    "    for i in emails_data:\n",
    "        email_structure = get_email_structure(i)\n",
    "        result[email_structure] += 1\n",
    "    return result\n",
    "\n",
    "ham_struct = structures_counter(ham_data)\n",
    "print(ham_struct)\n",
    "spam_struct = structures_counter(spam_data)\n",
    "for header, value in spam_data[0].items():\n",
    "    print(header, ':', value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 划分训练集和测试集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2400,)\n",
      "(2400,)\n"
     ]
    }
   ],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "x_np = np.array(ham_data + spam_data)\n",
    "y_np = np.array([0] * len(ham_data) + [1] * len(spam_data))\n",
    "x_train, x_test, y_train, y_test = train_test_split(x_np, y_np, test_size=0.2, random_state=42)\n",
    "print(x_train.shape)\n",
    "print(y_train.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 自定义类和函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.base import BaseEstimator, TransformerMixin\n",
    "from scipy.sparse import csr_matrix\n",
    "# 自定义HTML清理函数\n",
    "def html_to_plain_text(html):\n",
    "    text = re.sub('<head.*?>.*?</head>', '', html, flags=re.M | re.S | re.I)\n",
    "    text = re.sub('<a\\s.*?>', ' HYPERLINK ', text, flags=re.M | re.S | re.I)\n",
    "    text = re.sub('<.*?>', '', text, flags=re.M | re.S)\n",
    "    text = re.sub(r'(\\s*\\n)+', '\\n', text, flags=re.M | re.S)\n",
    "    return unescape(text)\n",
    "\n",
    "# 自定义email文件转文本函数\n",
    "def email_to_text(email_data):\n",
    "    html = None\n",
    "    for part in email_data.walk():\n",
    "        ctype = part.get_content_type()\n",
    "        if ctype not in (\"text/plain\", \"text/html\"):\n",
    "            continue\n",
    "        try:\n",
    "            content = part.get_content()\n",
    "        except:  # 解决编码问题\n",
    "            content = str(part.get_payload())\n",
    "        if ctype == \"text/plain\":\n",
    "            return content\n",
    "        else:\n",
    "            html = content\n",
    "    if html:\n",
    "        return html_to_plain_text(html)\n",
    "\n",
    "# 数据清洗\n",
    "# 自定义email转词次数类\n",
    "class EmailToWordCounterTransformer(BaseEstimator, TransformerMixin):\n",
    "    def __init__(self, strip_headers=True, lower_case=True, replace_urls=True,\n",
    "                 replace_numbers=True, remove_punctuation=True, stemming=True):\n",
    "        self.strip_headers = strip_headers\n",
    "        self.lower_case = lower_case\n",
    "        self.replace_urls = replace_urls\n",
    "        self.replace_numbers = replace_numbers\n",
    "        self.remove_punctuation = remove_punctuation\n",
    "        self.stemming = stemming\n",
    "        self.extractor = URLExtract()\n",
    "        self.stemmer = nltk.PorterStemmer()\n",
    "\n",
    "    def fit(self, x, y=None):\n",
    "        return self\n",
    "\n",
    "    def transform(self, x, y=None):\n",
    "        x_transformed = []\n",
    "        for i in x:\n",
    "            text = email_to_text(i) or ''\n",
    "            if self.lower_case:\n",
    "                text = text.lower()\n",
    "            if self.replace_urls:\n",
    "                urls = list(set(self.extractor.find_urls(text)))\n",
    "                urls.sort(key=lambda url: len(url), reverse=True)\n",
    "                for url in urls:\n",
    "                    text = text.replace(url, 'URL')\n",
    "            if self.replace_numbers:\n",
    "                text = re.sub(r'\\d+(?:\\.\\d*(?:[eE]\\d+))?', 'NUMBER', text)\n",
    "            if self.remove_punctuation:\n",
    "                text = re.sub(r'\\W+', ' ', text, flags=re.M)\n",
    "            word_counts = Counter(text.split())\n",
    "            if self.stemming:\n",
    "                stemmed_word_counts = Counter()\n",
    "                for word, count in word_counts.items():\n",
    "                    stem_word = self.stemmer.stem(word)\n",
    "                    stemmed_word_counts[stem_word] += count\n",
    "                word_counts = stemmed_word_counts\n",
    "            x_transformed.append(word_counts)\n",
    "        return np.array(x_transformed)\n",
    "\n",
    "# 数据处理\n",
    "# 自定义词次数转向量类\n",
    "class WordCounterToVectorTransformer(BaseEstimator, TransformerMixin):\n",
    "    def __init__(self, vocabulary_size=1000):\n",
    "        self.vocabulary_size = vocabulary_size\n",
    "        self.most_common_ = []\n",
    "        self.vocabulary_ = {}\n",
    "\n",
    "    def fit(self, x, y=None):\n",
    "        total_count = Counter()\n",
    "        for word_count in x:\n",
    "            for word, count in word_count.items():\n",
    "                total_count[word] += min(count, 10)\n",
    "        most_common = total_count.most_common()[:self.vocabulary_size]\n",
    "        self.most_common_ = most_common\n",
    "        self.vocabulary_ = {word: index + 1 for index, (word, count) in enumerate(most_common)}\n",
    "        return self\n",
    "\n",
    "    def transform(self, x, y=None):\n",
    "        rows = []\n",
    "        cols = []\n",
    "        data = []\n",
    "        for index, word_count in enumerate(x):\n",
    "            for word, count in word_count.items():\n",
    "                rows.append(index)\n",
    "                cols.append(self.vocabulary_.get(word, 0))\n",
    "                data.append(count)\n",
    "        return csr_matrix((data, (rows, cols)), shape=(len(x), self.vocabulary_size + 1))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 特征工程流水线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 特征工程流水线\n",
    "from sklearn.pipeline import Pipeline\n",
    "\n",
    "preprocess_pipeline = Pipeline([\n",
    "    ('email_to_wordcount', EmailToWordCounterTransformer()),\n",
    "    ('wordcount_to_vector', WordCounterToVectorTransformer())\n",
    "])\n",
    "# 训练集数据预处理\n",
    "x_train_finished = preprocess_pipeline.fit_transform(x_train)\n",
    "# 测试集数据预处理\n",
    "x_test_finished = preprocess_pipeline.transform(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "logistic精度：0.96875\n",
      "logistic召回率：0.9789473684210527\n",
      "forest精度：1.0\n",
      "forest召回率：0.9157894736842105\n",
      "Bagging精度：0.9655172413793104\n",
      "Bagging召回率：0.8842105263157894\n"
     ]
    }
   ],
   "source": [
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.ensemble import BaggingClassifier, RandomForestClassifier\n",
    "from sklearn.metrics import precision_score, recall_score\n",
    "\n",
    "# logistc回归模型\n",
    "lg_reg = LogisticRegression(solver=\"liblinear\", random_state=42)\n",
    "lg_reg.fit(x_train_finished, y_train)\n",
    "y_test_lg_pred = lg_reg.predict(x_test_finished)\n",
    "lg_acs = precision_score(y_test, y_test_lg_pred)\n",
    "lg_rcs = recall_score(y_test, y_test_lg_pred)\n",
    "print('logistic精度：{}'.format(lg_acs))\n",
    "print('logistic召回率：{}'.format(lg_rcs))\n",
    "# 随机森林分类模型\n",
    "rf_clf = RandomForestClassifier(random_state=42)\n",
    "rf_clf.fit(x_train_finished, y_train)\n",
    "y_test_rf_pred = rf_clf.predict(x_test_finished)\n",
    "rf_acs = precision_score(y_test, y_test_rf_pred)\n",
    "rf_rcs = recall_score(y_test, y_test_rf_pred)\n",
    "print('forest精度：{}'.format(rf_acs))\n",
    "print('forest召回率：{}'.format(rf_rcs))\n",
    "# Bagging分类模型\n",
    "bg_clf = BaggingClassifier(random_state=42)\n",
    "bg_clf.fit(x_train_finished, y_train)\n",
    "y_test_bg_pred = bg_clf.predict(x_test_finished)\n",
    "bg_acs = precision_score(y_test, y_test_bg_pred)\n",
    "bg_rcs = recall_score(y_test, y_test_bg_pred)\n",
    "print('Bagging精度：{}'.format(bg_acs))\n",
    "print('Bagging召回率：{}'.format(bg_rcs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结\n",
    "  * 根据上述结果所示，随机森林分类模型精度最高，并且达到了1，logistics回归模型召回率最高，根据需求选择合适模型\n",
    "  * 总体下来，步骤如下\n",
    "  * 加载数据并纵观数据大局\n",
    "  * 对数据集拆分成训练集和测试集\n",
    "  * 数据清洗，将emai文件转换称纯文本\n",
    "  * 数据处理转换，对邮件的文本内容进行分词处理\n",
    "  * 通过词汇表和邮件单词计数统计，将单词计数转化成向量矩阵\n",
    "  * 把数据清洗和数据处理封装成两个转换器\n",
    "  * 通过流水线来自动化处理数据\n",
    "  * 使用逻辑回归线性分类器、随机森林分类器和Bagging分类器进行模型训练\n",
    "  * 在测试集上得到精度/召回率，比较模型"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
